package com.miniapp.interceptor;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.miniapp.context.UserContext;
import com.miniapp.entity.R;
import com.miniapp.entity.User;
import com.miniapp.handler.exception.BusinessException;
import com.miniapp.properties.ApiAuthProperties;
import com.miniapp.util.MiniAppUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Map;

/**
 * 接口权限拦截器（可配置白名单）
 */
@Slf4j
@Component
public class AuthInterceptor implements HandlerInterceptor {

    /**
     * 路径匹配器（支持yml中配置的Ant风格白名单，如/api/auth/**）
     */
    private final AntPathMatcher pathMatcher = new AntPathMatcher();

    @Autowired
    private ApiAuthProperties apiAuthProperties;

    @Autowired
    private MiniAppUtil miniAppUtil;

    @Autowired
    private ObjectMapper objectMapper;

    /**
     * 请求拦截核心逻辑
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 1. 获取当前请求路径
        String requestPath = request.getRequestURI();
        log.info("拦截请求：{}", requestPath);

        // 2. 白名单接口直接放行（如小程序登录接口 /api/auth/wechat/login）
        if (isInWhitelist(requestPath)) {
            log.info("白名单接口，直接放行：{}", requestPath);
            return true;
        }

        // 3. 非白名单接口：从请求头获取token（小程序需按此格式传递）
        String token = getTokenFromHeader(request);

        try {
            // 4. 调用现有工具类验证token（复用generateToken的密钥和逻辑）
            Map<String, Object> payload = miniAppUtil.verifyToken(token);

            // 5. 提取用户信息（供后续接口使用，如@RequestAttribute获取）
            User user = miniAppUtil.extractUserFromPayload(payload);
            // 存入request，接口可直接获取
            request.setAttribute("loginUser", user);
            // 将用户信息存入ThreadLocal
            UserContext.setUser(user);
            // 6. token验证通过，放行请求
            return true;
        } catch (BusinessException e) {
            // 7. 令牌验证失败（过期/无效），返回401错误
            sendErrorResponse(response, HttpStatus.UNAUTHORIZED.value(), e.getMessage());
            return false;
        }
    }

    /**
     * 检查请求是否在白名单中（支持Ant风格路径，如/api/auth/**）
     */
    private boolean isInWhitelist(String requestPath) {
        List<String> whitelist = apiAuthProperties.getWhitelist();
        if (whitelist == null || whitelist.isEmpty()) {
            log.warn("拦截请求：{}（白名单为空）", requestPath);
            return false;
        }
        for (String whitePath : whitelist) {
            if (pathMatcher.match(whitePath, requestPath)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 从请求头获取token（小程序需传递：Authorization: Bearer {token}）
     */
    private String getTokenFromHeader(HttpServletRequest request) {
        String authHeader = request.getHeader("Authorization");
        if (StringUtils.hasText(authHeader) && authHeader.startsWith("Bearer ")) {
            // 截取"Bearer "前缀（共7个字符），获取纯token
            return authHeader.substring(7);
        }
        // 若未携带token，抛出异常（由verifyToken处理）
        throw new BusinessException("请先登录（未携带令牌）");
    }

    /**
     * 向小程序返回JSON格式错误响应（统一前端处理逻辑）
     */
    private void sendErrorResponse(HttpServletResponse response, int status, String message) throws Exception {
        // 构建标准错误响应（使用R.error()方法）
        R<?> errorResult = R.error(message);
        // 可根据需要设置响应码（如果R对象支持）
        errorResult.setCode(status);
        // 设置响应头
        response.setContentType("application/json;charset=UTF-8");
        response.setStatus(status);
        // 转换为JSON并写入响应
        response.getWriter().write(objectMapper.writeValueAsString(errorResult));
    }

    /**
     * 请求处理完成后执行
     * 清除ThreadLocal中的用户信息，避免内存泄漏
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 必须清除，否则线程池复用会导致用户信息错乱
        UserContext.clear();
    }
}